#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_CHAR 20   //学号和姓名的最大字符数
#define MAX_NUM 5000  //最大学生数

//结构体保存学生信息
struct student{
    char student_ID[MAX_CHAR];
    char student_name[MAX_CHAR];
    int student_age;
    char student_sex;//用来储存性别 F代表女 M代表男
    double score[5];//高数，英语，语文，体育，程序设计
};

//以下为函数声明
void help(void);
void seek(int * n, struct student * stu);
void modify(int * n, struct student * stu);
void insert(int * n, struct student * stu);
void del(int * n, struct student * stu);
void show(int i, struct student * stu);
void menu(int * n, struct student * stu, int * k);
void rank(int * n, struct student * stu);
void sub_rank(int * n, int * k, struct student * r_stu);
void flunk(int * n, struct student * stu);
void glance(int * n, struct student * stu);

//主函数
int main (void)
{
    int n = 0;//n代表已经存进去的学生数
    int k = 1;//菜单标志，用来结束程序
    struct student stu[MAX_NUM];//创建一个结构体变量用来储存学生信息
    while (k)
        {
            menu(&n, stu, &k);
        }
    printf("感谢您使用本程序，欢迎下次使用！");
    return 0;
}

//使用说明
void help(void)
{
    printf("\t欢迎使用学生管理系统帮助！\n");
    printf("\t初次进入系统，请先选择录入学生信息\n");
    printf("\t按照菜单提示输入数字\n");
    printf("\t感谢您的使用！\n");
}

//录入学生信息
void insert(int * n, struct student * stu)
{
    void show(int i, struct student * stu);//用来确定信息是否正确
    int i,j;//i 和 j均为迭代器
    int m;//m表示想加入的学生数
    int flag;//用来确定循环是否继续进行
    i = *n;
    printf("请输入待增加的学生数：\n");
    scanf("%d",&m);
    if(m>0)//确保加入的学生数是一个正数
    {
        do
        {
            flag = 1;//确保能进入循环
            while(flag)
            {
                flag = 0;//避免无限循环
                printf("请输入第%d位学生的学号：\n",i + 1);//i + 1避免了第0位学生的情况
                scanf("%s", stu[i].student_ID);
                for (j = 0; j < i; j++)
                {
                    if( strcmp(stu[i].student_ID, stu[j].student_ID) == 0)//strcmp函数比较两个字符串的长度，长度一致输出为0，以防止重复录入
                    {
                        printf("该学号已存在，请检查您输入的学号，重新输入！\n");
                        flag = 1;//确保能进入循环重新输入
                        break;
                    }
                }
            }
            printf("请输入第%d个学生的姓名：\n", i + 1);
            scanf("%s", stu[i].student_name);
            printf("请输入第%d个学生的年龄：\n", i + 1);
            scanf("%d", &stu[i].student_age);
            fflush(stdin);
            printf("请输入第%d个学生的性别：\n", i + 1);
            scanf("%c", &stu[i].student_sex);
            printf("请输入第%d个学生的高数成绩：\n", i + 1);
            scanf("%lf", &stu[i].score[0]);
            printf("请输入第%d个学生的英语成绩：\n", i + 1);
            scanf("%lf", &stu[i].score[1]);
            printf("请输入第%d个学生的体育成绩：\n", i + 1);
            scanf("%lf", &stu[i].score[2]);
            printf("请输入第%d个学生的语文成绩：\n", i + 1);
            scanf("%lf", &stu[i].score[3]);
            printf("请输入第%d个学生的程序设计成绩：\n", i + 1);
            scanf("%lf", &stu[i].score[4]);
            printf("请检查您所输入的数据是否正确：\n");
            show(i, stu);//重新将录入的信息
           if (0 == flag)
           {
               i++;//录入下一个学生信息
           }
        }while (i < *n + m);//确保不会多录入信息
    }
    *n += m;//录入完毕，更新已经录入的学生人数
    printf("该学生的信息已录入完毕！\n");
}

//显示信息（在函数内部使用，为了减少代码量，增强程序模块化)
void show(int i, struct student * stu)
{
    printf("学号：%s\n", stu[i].student_ID);
    printf("姓名：%s\n年龄：%d\n", stu[i].student_name, stu[i].student_age );
    printf("性别：%c\n高数：%f\n", stu[i].student_sex, stu[i].score[0]);
    printf("英语：%f\n体育：%f\n", stu[i].score[1], stu[i].score[2]);
    printf("语文：%f\n程序设计：%f\n", stu[i].score[3], stu[i].score[4]);
}

//查找学生信息
void seek(int * n, struct student * stu)
{
    void show(int i, struct student * stu);
    int i;//迭代器
    int item;//迭代器，用于switch语句
    int flag;//一个标志，用来检测学生是否存在
    char s1[21];
    printf("--------------------------\n");
    printf("--------1.按学号查询--------\n");
    printf("--------2.按姓名查询--------\n");
    printf("--------3.退出本菜单--------\n");
    printf("--------------------------\n");
    while (1)//不需要入口条件，直接进入循环
    {
        printf("请选择子菜单序号：\n");
        scanf("%d", &item);
        flag = 0;//对标志进行初始化
        switch (item)
        {
            case 1:
                printf("请输入要查询的学生的学号：\n");
                scanf("%s",s1);
                for( i = 0; i < *n; i++)//遍历数组
                {
                    if (strcmp(s1, stu[i].student_ID) == 0)//字符串比较函数，用来确认所要查询的学生就是该学生
                    {
                        flag = 1;//修改标志，避免后续错误输出
                        show(i, stu);//输出该学生信息
                        break;
                    }
                }
                    if (0 == flag)//检查标志，判断是否存在
                    {
                        printf("该学号不存在！\n");
                    }
                    break;
            case 2:
                printf("请输入要查询的学生的姓名：\n");
                scanf("%s", s1);
                for (i = 0; i < *n; i++)//遍历数组
                {
                    if (strcmp(s1, stu[i].student_name) == 0)//字符串比较函数，用来确认所要查询的学生就是该学生
                    {
                        flag = 1;//修改标志，避免后续错误输出
                        show(i, stu);//输出该学生信息
                        break;
                    }
                }
                    if (0 == flag)//检查标志，判断是否存在
                    {
                            printf("该姓名不存在！\n");
                    }
                    break;
            case 3:
                return;//函数调用结束，返回上一级
            default:
                printf("请在1-3之间选择\n");
        }
    }
}

//修改学生信息
void modify(int * n, struct student * stu)
{
    int i;//迭代器
    int item;//迭代器，用于switch语句
    int num = 0;//当作标志，用来判断是否存在该学生
    char s1[MAX_CHAR];//临时字符数组，用来储存学号
    int flag = 1;//当作标志，用来判断是否修改了信息
    printf("请输入要修改的学生的学号:\n");
    scanf("%s", s1);
    for (i = 0; i < *n; i++)//遍历数组，寻找需要的数据
    {
        if (strcmp(stu[i].student_ID, s1) == 0)//字符串比较函数，用来确认所要查询的学生就是该学生
        {
            num = i;//将i的值赋值给num，避免num的值不变，造成后续程序识别标志失败，输出错误
            printf("------------------\n");
            printf("1.修改姓名\n");
            printf("2.修改年龄\n");
            printf("3.修改性别\n");
            printf("4.修改高数成绩\n");//高数，英语，体育，语文，程序设计
            printf("5.修改英语成绩\n");
            printf("6.修改体育成绩\n");
            printf("7.修改语文成绩\n");
            printf("8.修改程序设计成绩\n");
            printf("9.退出本菜单！\n");
            printf("------------------\n");
            while (1)//无需入口检测，直接进入循环
            {
                printf("请选择子菜单编号:\n");
                scanf("%d", &item);
                switch (item)
                {
                case 1:
                    printf("请输入新的姓名:\n");
                    scanf("%s", stu[num].student_name);
                    break;
                case 2:
                    printf("请输入新的年龄:\n");
                    scanf("%d", &stu[num].student_age);
                    break;
                case 3:
                    printf("请输入新的性别:\n");
                    fflush(stdin);//避免因为缓冲区还有回车键，从而影响性别的录入
                    scanf("%c", &stu[num].student_sex);
                    break;
                case 4:
                    printf("请输入新的高数成绩:\n");
                    scanf("%lf", &stu[num].score[0]);
                    break;
                case 5:
                    printf("请输入新的英语成绩:\n");
                    scanf("%lf", &stu[num].score[1]);
                    break;
                case 6:
                    printf("请输入新的体育成绩:\n");
                    scanf("%lf", &stu[num].score[2]);
                    break;
                case 7:
                    printf("请输入新的语文成绩:\n");
                    scanf("%lf", &stu[num].score[3]);
                    break;
                case 8:
                    printf("请输入新的程序设计成绩:\n");
                    scanf("%lf", &stu[num].score[4]);
                    break;
                case 9:
                    return;
                default:
                    printf("请在1-9之间选择\n");
                    flag = 0;
                    break;
                }
                if(1 == flag)
                {
                printf("修改完毕！\n");
                }
            }
        }

    }
    if(0 == num)//检查标志，查看是否存在
    {
        printf("没有该学生学号!!!\n");
    }
}

//删除学生信息函数
void del(int * n, struct student * stu)
{
    int i, j;//迭代器
    int flag = 0;//作为标志，检查是否存在
    char s1[MAX_CHAR];//临时字符数组，用来储存学号
    printf("请输入要删除学生的学号:\n");
    scanf("%s", s1);
    for (i = 0; i < *n; i++)
    {
        if (strcmp(stu[i].student_ID, s1) == 0)//字符串函数，比较两个字符串的大小，确定要删除的学生是否为该学生
        {
            flag = 1;//改变标志
            for (j = i; j < *n - 1; j++)//要删除学生后面的学生往前移一位,(*n - 1)代表剩下的学生数
            {
                stu[j] = stu[j + 1];
            }
        }
    }
        if (0 == flag)//检查标志
        {
            printf("该学号不存在!!!\n");
        }
        if (1 == flag)//检查标志
        {
            printf("删除成功！！！\n");
            (*n)--;//删除成功，学生人数减1
        }
}

//不及格学生信息
void flunk(int * n, struct student * stu)
{
    int i;//迭代器
    int j = 0;//统计不及格的科目的数目
    const int SCORE_LINE = 60;//100分制的60分及格线
    printf("共有以下学生不及格科目超过两门，以及其对应的不及格科目：\n");
    for (i = 0; i < *n; i++)//遍历学生数据
    {
        //统计所有的不及格的科目数量
        if(stu[i].score[0] < SCORE_LINE)
            j++;
        if(stu[i].score[1] < SCORE_LINE)
            j++;
        if(stu[i].score[2] < SCORE_LINE)
            j++;
        if(stu[i].score[3] < SCORE_LINE)
            j++;
        if(stu[i].score[4] < SCORE_LINE)
            j++;
        if (j > 2)//该同学不及格科目超过两科
        {
            printf("学号：%s \t 姓名：%s \t ", stu[i].student_ID, stu[i].student_name);
            if(stu[i].score[0] < SCORE_LINE)//高数，英语，语文，体育，程序设计
                printf("高数：%f \t ", stu[i].score[0]);
            if(stu[i].score[1] < SCORE_LINE)
                printf("英语：%f \t ", stu[i].score[1]);
            if(stu[i].score[2] < SCORE_LINE)
                printf("语文：%f \t ", stu[i].score[2]);
            if(stu[i].score[3] < SCORE_LINE)
                printf("体育：%f \t ", stu[i].score[3]);
            if(stu[i].score[4] < SCORE_LINE)
                printf("程序设计：%f \t ", stu[i].score[4]);
            printf("\n");
        }
        j = 0;
    }
    
}

//浏览学生信息
void glance(int * n, struct student * stu)
{
    void show(int i, struct student * stu);
    int i, j;//均为迭代器
    struct student temp;//临时储存数据的结构体
    for(i = 1; i < *n; i++)//冒泡法的外循环
    {
        for(j = 0; j < *n - i; j++)//冒泡法的内循环
        {
            if( strcmp(stu[j].student_ID, stu[j + 1].student_ID) > 0)//将学号两两对比，分出大小值，按升序排列
            {
                temp = stu[j];
                stu[j] = stu[j + 1];
                stu[j + 1] = temp;
            }
        }
    }
    for(i =0; i < *n; i++)//将学号按照从小到大排列
    {
        show(i, stu);
    }
}

//冒泡法排序算法模版
void sub_rank(int * n, int * k, struct student * r_stu)
{
    int i, j, s;//均为迭代器
    struct student temp;//临时储存数据的结构体
    for(i = 1; i < *n; i++)//冒泡法的外循环
    {
        for(j = 0; j < *n - i; j++)//冒泡法的内循环
        {
            if( r_stu[j].score[*k - 1] > r_stu[j + 1].score[*k - 1])//将数组内的值两两对比，分出大小值，按升序排列
            {
                temp = r_stu[j];
                r_stu[j] = r_stu[j + 1];
                r_stu[j + 1] = temp;
            }
        }
    }
    for(s = *n - 1;s >= 0; s--)
    {
        //按照成绩由高到低输出个人信息
        printf("成绩：%f \t 姓名：%s \t ", r_stu[s].score[*k - 1], r_stu[s].student_name);
        printf("学号：%s \t 性别：%c \t ", r_stu[s].student_ID, r_stu[s].student_sex);
        printf("年龄：%d \n", r_stu[s].student_age);
    }
}

//用冒泡法对单科成绩进行排序
void rank(int * n, struct student * stu)
{
    void sub_rank(int * n, int * k, struct student * r_stu);
    int k;//迭代器，用于switch语句
    int i;
    struct student r_stu[MAX_NUM];
    for ( i = 0; i < *n; i++)//用于把数据拷贝到一个别的结构体里，避免影响已经录入的数据
    {
        r_stu[i] = stu[i];
    }
    printf("请输入您想查询的学科： \n");
    printf("1.高数\n");
    printf("2.英语\n");
    printf("3.语文\n");
    printf("4.体育\n");
    printf("5.程序设计\n");
    printf("6.退出本程序\n");
    while(1)
    {
        printf("请输入子菜单编号：\n");
        scanf("%d", &k);
        switch(k)
        {
            case 1:
                printf("高数单科成绩按升序排为：\n");
                sub_rank(n, &k, r_stu);
                break;
            case 2:
                printf("英语单科成绩按升序排为：\n");
                sub_rank(n, &k, r_stu);
                break;
            case 3:
                printf("语文单科成绩按升序排为：\n");
                sub_rank(n, &k, r_stu);
                break;
            case 4:
                printf("体育单科成绩按升序排为：\n");
                sub_rank(n, &k, r_stu);
                break;
            case 5:
                printf("程序设计单科成绩按升序排为：\n");
                sub_rank(n, &k, r_stu);
                break;
            case 6:
                return;
            default:
                printf("请在1～5之间输入数字\n");
                break;
        }
    }
}

//主菜单
void menu(int * n, struct student * stu, int * k)
{
    int num;//迭代器，用于switch语句
    printf("\n\t\t******************************** \n");
    printf("\t\t       学生成绩信息管理系统      \n\n");
    printf("\t\t        0、系统说明            \n\n");
    printf("\t\t        1、录入学生信息         \n\n");
    printf("\t\t        2、浏览学生信息         \n\n");
    printf("\t\t        3、查询学生信息         \n\n");
    printf("\t\t        4、修改学生信息         \n\n");
    printf("\t\t        5、单科成绩排序         \n\n");
    printf("\t\t        6、删除学生信息         \n\n");
    printf("\t\t        7、未达标学生名单       \n\n");
    printf("\t\t        8、退出系统            \n\n");
    printf("\t\t******************************** \n\n");
    printf("请选择您需要办理的业务：\n");
    scanf("%d", &num);
    switch (num)
    {
        case 0:
            help();
            break;
        case 1:
            insert(n, stu);
            break;
        case 2:
            glance(n, stu);
            break;
        case 3:
            seek(n, stu);
            break;
        case 4:
            modify(n, stu);
            break;
        case 5:
            rank(n, stu);
            break;
        case 6:
            del(n, stu);
            break;
        case 7:
            flunk(n, stu);
            break;
        case 8:
            *k = 0;
            printf("即将退出程序！\n");
            break;
        default:
            printf("请在1～8之间输入数字");
            break;
    }
    
}
